L.Element = L.Class
		.extend({
			includes : L.Mixin.Events,

			options : {},

			initialize : function(attrs, options) {
				L.setOptions(this, options);
				this.deserialize(attrs || {});
				this._fireEvents = true;
				/* set id */
				this.id = uniqueId(attrs);
			},

			onAdd : function(canvas) {
				this._canvas = canvas;

				this.draw();
				if (canvas.editing()) {
					this.bindEvents();
				}
			},

			addTo : function(canvas) {
				canvas.addElement(this);
				return this;
			},

			onRemove : function(canvas) {
				this.erase();
				this._canvas = null;
			},

			onUpdate : function() {
				this.erase();
				this.draw();
				return this;
			},

			serialize : function() {
				var attrs = {};
				L.Util.extend(attrs, this._attrs);
				if (typeof attrs.path == "object") {
					attrs.path = this.svg_path_to_string(attrs.path);
				}
				return attrs;
			},

			deserialize : function(attrs) {
				this._attrs = attrs;
				if (this._canvas) {
					this.draw();
				}
			},

			attr : function() {
				if (!this._element) {
					var args = Array.prototype.slice.call(arguments);
					this.deserialize(args[0]);
					console
							.log("[warning]attr() aun no esta implementada para ser llamada antes de ser agrega a un canvas, argumento fue deserializado");
					return;
				}
				var resp = this._element.attr.apply(this._element, arguments);
				var attrs = this._element.attr();
				attrs.type = this._attrs.type;
				attrs.logical_type = this._attrs.logical_type;
				attrs.path = this._attrs.path;
				attrs.layer = this._attrs.layer;
				attrs.id = this._attrs.id;
				attrs['stroke-width'] = this._attrs['stroke-width'];
				if (attrs.logical_type == "text") {
					attrs.box_width = this._attrs.box_width;
					attrs.box_height = this._attrs.box_height;
				}
				this._attrs = attrs;
				if (this._fireEvents) {
					this.fire('change', this);
				}
				return resp != this._element ? resp : this;
			},

			noEventsAttr : function() {
				this._fireEvents = false;
				var resp = this.attr.apply(this, arguments);
				this._fireEvents = true;
				return resp != this._element ? resp : this;
			},

			draw : function() {
				var type = this._attrs.type;
				var raphael = this._canvas.getRaphael();

				// create a Raphael Element
				this._element = raphael[type]();
				this._element.id = this._attrs.id;
				this._element.attr(this._attrs);
				this._element.data('elementId', this._attrs.id);

				// set scale
				/*
				 * if(this._scale != 1){ element.scale(this._scale); }
				 */
			},

			erase : function() {
				if (this._element) {
					this._element.remove();
					this._element = null;
				}
			},

			scale : function(scale) {
				// no se guarda la propiedad escalada
				var strokeWidth = this._attrs["stroke-width"] * scale;
				this._element.attr("stroke-width", strokeWidth);
			},

			getRaphaelElement : function() {
				if (this._border)
					return [ this._element, this._border ];
				return [ this._element ];
			},

			bindEvents : function() {
				var self = this;
				/*
				 * var events = element.events || []; for(var i=0,
				 * len=events.length; i<len; i++){ events[i].unbind(); }
				 * element.events = [];
				 * 
				 * element.drag( //dragMove function(){ var args =
				 * Array.prototype.slice.apply(arguments); args.unshift(this);
				 * self._tool.elementDragMove.apply(self._tool, args); },
				 * //dragStart function(){ var args =
				 * Array.prototype.slice.apply(arguments); args.unshift(this);
				 * self._tool.elementDragStart.apply(self._tool, args); },
				 * //dragEnd function(){ var args =
				 * Array.prototype.slice.apply(arguments); args.unshift(this);
				 * self._tool.elementDragEnd.apply(self._tool, args); } );
				 */
			},

			unbindEvents : function() {

			},

			intersectWithBox : function(box) {
				var elbox = this._element.getBBox();
				var inter = Raphael.isBBoxIntersect(elbox, box);
				return inter;
			},

			intersectWithPath : function(path) {
				var inter = Raphael.pathIntersection(this._element.attrs.path,
						path.attrs.path);
				// console.log("this._element[0]");console.log(this._element[0]);
				// console.log("box_path");console.log(box_path);
				// console.log("inter");console.log(inter);
				return inter.length > 0;
			},

			drawBorder : function() {
				this.removeBorder();
				this._border = this._element.glow({
					color : this._element.attr('stroke')
				});
			},

			removeBorder : function() {
				if (this._border) {
					this._border.remove();
					this._border = null;
				}
			},

			move : function(dx, dy) {
				this._element.transform('t' + dx + ',' + dy + '...');
				if (this._fireEvents) {
					this.fire('change', this);
				}
			},

			rotable : function() {
				var logical_type = this._attrs.logical_type;
				// console.log('rotable:logical_type');console.log(logical_type);
				if (logical_type == "checkbox_good"
						|| logical_type == "checkbox_bad") {
					return false;
				}
				return true;
			},

			scalable : function() {
				var logical_type = this._attrs.logical_type;
				if (logical_type == "checkbox_good"
						|| logical_type == "checkbox_bad") {
					return false;
				}
				return true;
			},

			/*
			 *  // Create a deep copy of strokes if // the sketch is scaled, and
			 * alter the // stroke-width property of the copy var _stroke =
			 * stroke; if(this._scale != 1){ _stroke = jQuery.extend(true, {},
			 * stroke); _stroke["stroke-width"] *= this._scale; }
			 * 
			 */
			svg_path_to_string : function(path) {
				var str = "";
				for (var i = 0, n = path.length; i < n; i++) {
					var point = path[i];
					str += point[0] + point.slice(1).join(',');
				}
				return str;
			},

		});
